/** --------------------- 基本类型 --------------------- */
const str: string = "hello typescript";
const bool: boolean = true;
const num: number = 123;
// const num2: number = "123"; // 报错

// 数组
const arr1: number[] = [1, 2, 3];
const arr2: Array<number> = [4, 5, 6]; // 数组泛型

// 元组 Tuple：允许表示一个已知元素数量和类型的“数组”
let x : [string, number]
x = ['hello', 10];
// x = [10, 'hello']; // 报错

// 枚举
enum Color { Red, Green, Blue };
let c: Color = Color.Red; // 0
console.info(c);

// any
// 在某些阶段还不清楚类型的变量。拒绝写anyscript
let notSure: any = 4;
notSure = "123";
notSure = true;

// void:表示没有任何类型，一般用于函数没有返回值。声明变量可以，但是只能赋值undefined 或 null
function funcVoid(): void {
    // return 123; // 会报错
}

// Never
// 表示永不存在的值。
function infiniteLoop(): never {
    while(true) {}
}

interface A {
    label: number;
    key: number;
}
interface B {
    label: string;
    key: number;
}
interface C {
    id: string;
}

type TT = A & C;
const tt: TT = { label: 1, key: 1, id: "" };
type T = A & B; // 这里的label就是never



// Object,表示非原始类型。使用object，可以更好表示像Object.create这样的API。其他时候，避免直接使用
type create = (o: object) => void;

// 类型断言;注：在使用JSX时，只有as是被允许的
// 1.尖括号
let someValue: any = "this is string";
(<string>someValue).length;
// 2.as
(someValue as string).length;



/** --------------------- 接口 --------------------- */
interface LabelledValue {
    label: string;
    name?: string; // 可选
    readonly id: string; // 只读
    [key: string]: any; //额外的类型检查
}

// 函数类型
interface Func {
    (key: string, value: string): void;
}

type Func1 = (key: string, val: number) => void

const func: Func = () => {}
const func1: Func1 = (_k, _v) => {}

// 可索引的类型
const label: LabelledValue["label"] = "21"

// 类类型 implements
interface ClockInterface {
    currentTime: Date;
}
class Clock implements ClockInterface {
    currentTime: Date = new Date();
    constructor(_h: number, _m: number) { }
}

// 继承接口 extends
interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

let square: Square = {
    color: "red",
    sideLength: 12,
}

// 混合类型。对象同时具有上面提到的多种类型
// 接口继承类


/** --------------------- 泛型 --------------------- */
// T帮助我们捕获用户传入的类型（比如：number），之后我们就可以使用这个类型。
// 之后我们再次使用了 T当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了
// 这允许我们跟踪函数里使用的类型的信息。
function identity<T>(arg: T): T {
    return arg;
}
// 不同于any，它不会丢失信息
let output = identity<string>("myString"); // 明确的指定了T是string类型

// 泛型变量
function loggingIdentity<T>(arg: T): T {
    // console.log(arg.length);  // Error: T doesn't have .length
    return arg;
}
// 泛型约束
function log<T extends { length: number }>(arg: T): T {
    console.info(arg.length);
    return arg;
}

// 泛型类
// class GenericNumber<T> {
//     zeroValue: T;
//     add: (x: T, y: T) => T;
// }

// let myGenericNumber = new GenericNumber<number>();
// myGenericNumber.zeroValue = 0;
// myGenericNumber.add = function(x, y) { return x + y; };

// 泛型接口对象
interface Global<T> {
    label: T;
}

const g: Global<string> = {
    label: ""
}


/** --------------------- 类型推断 --------------------- */
// 除了string、number、boolean，其他类型避免使用！！！！
let xxx = 1;
let arr = [123, "123"]
let obj = {
    k1: "v1",
    k2: 12,
}


/** --------------------- 类型兼容性 --------------------- */
interface Named {
    name: string;
}
let n: Named = { name: "" };
let y = { name: 'Alice', location: 'Seattle' };
n = y; // 可以赋值
// 检查函数参数
function greet(n: Named) {
    console.log('Hello, ' + n.name);
}
greet(y); // OK

// ！！！！ y有个额外的location属性，但这不会引发错误。 只有目标类型（这里是Named）的成员会被一一检查是否兼容。

let x1 = (a: number) => 0;
let y1 = (b: number, s: string) => 0;

y1 = x1; // OK
// x1 = y1; // Error

/** --------------------- 高级类型 --------------------- */
// 交叉类型 & 
// 联合类型 ｜ 

interface Bird {
    fly(): void;
    layEggs(): void;
}

interface Fish {
    swim(): void;
    layEggs(): void;
}
type TTT = Bird | Fish;

function getSmallPet(): Fish | Bird {
    return {
        fly: () => {},
        layEggs: () => {},
        swim: () => {}
    }
}

let pet = getSmallPet();
pet.layEggs(); // okay
// pet.swim();    // errors

// 类型保护
// if ((<Fish>pet).swim) {
//     (<Fish>pet).swim();
// }
// else {
//     (<Bird>pet).fly();
// }


// pet is Fish 类型谓词
// function isFish(pet: Fish | Bird): pet is Fish {
//     return (<Fish>pet).swim !== undefined;
// }
